package cn.shadow.aop;

import cn.shadow.aop.advice.Advice;
import cn.shadow.aop.advisor.Advisor;
import cn.shadow.aop.advisor.PointcutAdvisor;
import cn.shadow.aop.pointcut.Pointcut;
import cn.shadow.beans.BeanFactory;
import cn.shadow.beans.BeanFactoryAware;
import cn.shadow.beans.BeanPostProcessor;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 相当于是代理创建者
 * @author 冯啸迪
 */
public class AdvisorAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware {

    /**
     * Bean工厂
     */
    private BeanFactory beanFactory;

    /**
     * 所有列表
     */
    private List<Advisor> advisorList;

    /**
     * 是否加载过
     */
    private volatile boolean gettedAllAdvisors = false;


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {
        // 创建Bean对象并且初始化之后，应该干什么
        if (bean instanceof Advisor || bean instanceof Advice){
            // 用户自己定义的那种通知，切面什么的，其实不需要进行增强
            return bean;
        }
        List<Advisor> needAdvisor = getMatchedAdvisor(bean);

        if (CollectionUtils.isEmpty(needAdvisor)){
            bean = createProxyInstance(bean,beanName,needAdvisor);
        }
        // 根据列表创建代理对象
        return bean;
    }

    /**
     * 获取所有的Advisor，然后根据当前实例匹配那些Advisor要对Bean进行增强
     * @param object 对象
     * @return 返回切面对象
     * @throws Throwable 会抛出的异常
     */
    private List<Advisor> getMatchedAdvisor(Object object) throws Throwable {
        // 初次加载
        Class<?> clazz = object.getClass();
        if (!gettedAllAdvisors){
            synchronized (this){
                advisorList = beanFactory.getBeansByTypeList(Advisor.class);
                gettedAllAdvisors = true;
            }
        }
        List<Advisor> matchAdvisor = new ArrayList<>();
        Method[] methods = clazz.getMethods();
        for (Advisor advisor : advisorList){
            if (advisor instanceof PointcutAdvisor){
                if (isPointcutMatchBean((PointcutAdvisor) advisor, clazz, methods)){
                    matchAdvisor.add(advisor);
                }
            }

        }
        return matchAdvisor;
    }

    /**
     * 当前通知是否是这个Bean的
     * @param pa 切点通知
     * @param beanClass Bean的类型
     * @param methods 方法列表
     * @return 是否匹配
     */
    private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, Method[] methods) {
        // 先对比类
        Pointcut pc = pa.getPointcut();
        if (!pc.matchClass(beanClass)){
            return false;
        }
        for (Method method : methods){
            if (pc.matchMethod(method, beanClass)){
                return true;
            }
        }
        // 再对比方法

        return false;
    }

    /**
     * 创建代理的实例
     * @param bean Bean实例
     * @param beanName beanName
     * @param advisorList 通知列表
     * @return 代理对象
     */
    private Object createProxyInstance(Object bean, String beanName, List<Advisor> advisorList) throws Throwable {
        return AopProxyFactory.getDefaultAopProxyFactory().getAopProxy(bean, beanName, advisorList, beanFactory).getProxy();
    }

        @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
}
